/**
 * 文件名:  socket_utils.h
 * 作者:    wangtianliang
 * 日期:    2022-12-20
 * 功能:   
 * 	socket相关处理封装库
 */

#include "socket_utils.h"
//#include "debug.h"

/**CONSTANTS DEFINE**/
#define REDO 1
#define CONTINUE 0

/**SIGNALS DEFINE**/
#define ACCEPT_INTERRUPTED (errno == EINTR || errno == ECHILD)


//it returns file descriptor for the socket
int socket_open(int family, int type, int protocol)
{
	int tmp = 0;

	if ((tmp = socket(family, type, protocol)) < 0) {
		//DEBUG(LOG_WARNING, "**Error socket_open creation: %s.", strerror(errno));
		printf("**Error socket_open creation: %s.\n", strerror(errno));
	}

	return tmp;
}

//it assigns a local address to a socket
int socket_bind(int socket, const sa_st *address, socklen_t addrlen)
{
	int tmp = 0;

	if ((tmp = bind(socket, address, addrlen)) != 0) {
		//DEBUG(LOG_WARNING, "**Error in socket_bind: %s.", strerror(errno));
		printf("**Error in socket_bind: %s.\n", strerror(errno));
	}

	return tmp;
}

//set a socket in passive mode, in order to listen to a connection
int socket_listen(int socket, int backlog)
{
	char *ptr = NULL;
	int tmp = 0;

	if ((ptr = getenv("LISTENQ")) != NULL) {
		backlog = atoi(ptr);
	}

	if ((tmp = listen(socket, backlog)) < 0) {
		//DEBUG(LOG_WARNING, "**Error in socket_listen: %s.",strerror(errno));
		printf("**Error in socket_listen: %s.\n",strerror(errno));
	}

	return tmp;
}

//it manages a new connection after the end of three-way handshake
int socket_accept(int socket, sa_st *clientAddr, socklen_t *addrlen)
{
	int cfd = -1;
	int repeat = CONTINUE;

	do{
		cfd = accept(socket, clientAddr, addrlen);
		if (cfd >= 0) {
			break;
		}

		//check if there are signals
		if (ACCEPT_INTERRUPTED) {
			repeat=REDO;
		} else {
			//dbg_log the error condition
			//DEBUG(LOG_WARNING, "**Error in socket_accept: %s.", strerror(errno));
			printf("**Error in socket_accept: %s.\n", strerror(errno));
			
			break;
		}
	}while(repeat == REDO);

	return cfd;
}

//it establishes a new connection with a server
int socket_connect(int socket, const sa_st *serverAddress, socklen_t addrlen)
{
	int tmp = 0;

	if ((tmp = connect(socket, serverAddress, addrlen)) != 0) {
		//DEBUG(LOG_WARNING, "**Error in socket_connect: %s.", strerror(errno));
		printf("**Error in socket_connect: %s.\n", strerror(errno));
	}

	return tmp;
}

//设置socket addr
int socket_addr_set(struct sockaddr_in *psock, char *addr, int port)
{
	if (psock == NULL || addr == NULL || port <=0 || port >= SOCKET_PORT_MAX) {
		//DEBUG(LOG_WARNING, "param invalid psock=%p, addr=%p, port=%d", psock, addr, port);
		printf("param invalid psock=%p, addr=%p, port=%d\n", psock, addr, port);
		return -1;
	}

	memset((void*)psock, 0, sizeof(struct sockaddr_in)); //clear proxy_master address
	psock->sin_family = AF_INET;
	my_inet_pton(AF_INET, addr, &(psock->sin_addr.s_addr));
	psock->sin_port = htons(port);
	return 0;
}


//set a socket as closed
int socket_close(int socket)
{
	int tmp = 0;

	if (socket < 0)
		return 0;

	if ((tmp = close(socket)) != 0)
		//DEBUG(LOG_WARNING, "**Error in socket_close: %s.", strerror(errno));
        printf("**Error in socket_close: %s.\n", strerror(errno));

	return tmp;
}

//select
int my_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout)
{
	int repeat = CONTINUE;
	int tmp = 0;

	do {
		tmp = select(maxfdp1, readset, writeset, exceptset, timeout);
		if (tmp < 0) {
			//check if there are signals
			if (ACCEPT_INTERRUPTED) {
				repeat = REDO;
			} else {
				//dbg_log the error condition
				//DEBUG(LOG_WARNING, "**Error in my_select: %s.", strerror(errno));
                printf("**Error in my_select: %s.\n", strerror(errno));
			}
		} else {
			repeat = CONTINUE;
		}
	} while(repeat == REDO);

	return tmp;
}

//it tries to read from the socket
int socket_recv(int socket, void *buffer, int len, int flag)
{
	int tmp = 0;

	tmp = recv( socket, buffer, len, flag);
	if (tmp < 0) {
		//DEBUG(LOG_WARNING, "***Error in socket_recv: %s.", strerror(errno));
		printf("***Error in socket_recv: %s.\n", strerror(errno));
	}

	return tmp;
}

//it tries to send data to the socket
int socket_send(int socket, void *buffer, int len, int flags, int *epipeError)
{
	int tmp = send(socket, buffer, len, 0);
	*epipeError = 0;
	if (tmp < 0) {
		//DEBUG(LOG_WARNING, "***Error in socket_send: %s.", strerror(errno));
		printf("***Error in socket_send: %s.\n", strerror(errno));
		if (errno == EPIPE) {
			*epipeError = 1;
			//DEBUG(LOG_WARNING, "EPIPE.");
			printf("EPIPE error!.\n");
		}
	}

	return tmp;
}

//inet_pton
int my_inet_pton(int af, const char *strptr, void *addrptr)
{
	int status = inet_pton(af, strptr, addrptr);
	if (status <= 0) {
		//DEBUG(LOG_WARNING, "Error in inet_pton: %s.", strerror(errno));
		printf("Error in inet_pton: %s.\n", strerror(errno));
	}

	return status;
}

//inet_ntop
const char *my_inet_ntop(int af, const void *addrptr, char *strptr, size_t length)
{
	const char *strend = inet_ntop(af, addrptr, strptr, length);
	if (strend == NULL) {
		//DEBUG(LOG_WARNING, "Error in inet_ntop: %s.", strerror(errno));
		printf("Error in inet_ntop: %s.\n", strerror(errno));
	}

	return strend;
}

//将字符串类型的ip地址和端口，格式化成sddrinfo结构
int socket_addrinfo_get(struct addrinfo **pres, char *ip, char *port)
{
	int err = 0;
	struct addrinfo hints = {0};

	hints.ai_socktype = SOCK_STREAM;        /* tcp类型 */
	hints.ai_family = AF_UNSPEC;            /* 不区分v4、v6类型 */
	hints.ai_flags = AI_PASSIVE;            /* For wildcard IP address */
	hints.ai_protocol = IPPROTO_TCP;        /* tcp类型 */
	err = getaddrinfo(ip, port, &hints, pres);
	if (err) {
		//DEBUG(LOG_WARNING, "error %d: %s", err, gai_strerror(err));
		printf("error %d: %s\n", err, gai_strerror(err));
		return -1;
	}

	return 0;
}

//关闭addrinfo结构
void socket_addrinfo_free(struct addrinfo *res)
{
	freeaddrinfo(res);
}
